<template>
    <transition name="animate__animated " enter-active-class="animate__lightSpeedInLeft animate__fast "
        leave-active-class="animate__fadeOutRight animate__faster">
        <div class="loginInfo forbid-select" @click="close" v-if="showLogin" ref="loginRef">
            <div class="loginBg" @click.stop>
                <div class="flex-display-center">
                    <div class="title flex-display-center">{{ getWebInfo.websiteName }}</div>
                    <div class="change flex-display-center" v-if="showChangeType">
                        <el-icon :class="`${loginByQrCode ? 'rotate-180' : 'rotate-0'} cursor-pointer`"
                            @click="changeLoginType">
                            <component :is="getIcon('Switch')" />
                        </el-icon>
                    </div>
                </div>
                <div class="loginTypeContainer">
                    <div v-if="loginByQrCode"
                        class="animate__animated animate__lightSpeedInLeft animate__fast loginByQrcode">
                        <div class="tip">使用小程序扫码登录</div>
                        <div class="qrcodeBox">
                            <div v-if="!loginSuccess">
                                <img src="@/assets/image/logo.png" class="qrCodeLogo" alt="logo">
                                <canvas ref="qrcodeCanvas" class="qrcode" id="qrcode" />
                                <div class="expireTime" v-if="expireTime > 0">
                                    剩余过期时间<span class="expireSecond">{{ expireTime }}</span>秒
                                </div>

                                <div class="expireModel" v-if="!expireTime && status !== 0" @click="getQrImg">
                                    <div class="expireTime">二维码已过期</div>
                                    <div class="expireTip">已过期<br>点击重新获取</div>
                                </div>
                                <div class="expireModel" v-if="status === -1 && expireTime && !loginSuccess">
                                    <div class="expireTip">已扫码<br>等待手机确认</div>
                                </div>
                                <div class="expireModel" v-if="status === 0 && !loginSuccess" @click="getQrImg">
                                    <div class="expireTip">已取消<br>点击重新获取</div>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div v-else class="animate__animated animate__lightSpeedInLeft animate__fast loginByPassword">
                        <div class="item">
                            <div class="login-input">
                                <userInput @focus="focus(1)" @blur="blur(1)" v-model="loginForm.username"
                                    @keyup.enter="submitForm" id="username" background="transparent"
                                    @mousedown="checkCapslock" />
                                <div :class="`label flex-display-center blur-input ${showUserLabel ? '' : 'up-label'}`"
                                    key="u11">
                                    <el-icon>
                                        <component :is="getIcon('user')" />
                                    </el-icon>
                                    <span style="margin-top: 3px;">用户名/手机/邮箱/用户ID</span>
                                </div>
                            </div>
                        </div>
                        <div class="item">
                            <div class="login-input">
                                <input name="password" @keyup.enter="submitForm" autocomplete="off" id="pwd"
                                    @focus="focus(2)" @blur="blur(2)" @keydown.delete.prevent="clearPwd" type="password"
                                    @keyup.delete.prevent v-model="loginForm.password" @mousedown="checkCapslock" />
                                <div :class="`label flex-display-center blur-input ${showPwdLabel ? '' : 'up-label'}`"
                                    key="p1">
                                    <el-icon>
                                        <component :is="getIcon('lock')" />
                                    </el-icon>
                                    <span style="margin-top: 1px;">密码</span>
                                </div>
                                <el-icon class="login-button" @click="submitForm">
                                    <component :is="getIcon('right')" />
                                </el-icon>
                            </div>
                        </div>
                        <div class="message">
                            <transition enter-active-class="animate__animated animate__headShake"
                                leave-active-class="animate__animated animate__fadeOutRight animate__faster"
                                mode="out-in">
                                <span v-if="error" class="error" key="error">{{ errMsg }}</span>
                            </transition>

                            <transition enter-active-class="animate__animated animate__headShake"
                                leave-active-class="animate__animated animate__fadeOutLeft animate__faster"
                                mode="out-in">
                                <span v-if="capslock && !loginByQrCode" class="capital" key="capslock">大写锁定已开启</span>
                            </transition>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </transition>
    <div class="login-container forbid-select animate__animated animate__fadeIn animate__faster" v-if="showLogin"
        ref="loginModalRef" />
</template>

<script setup>
import { useWait, useRequest, useElLoading } from '@usehook'
import { pbEncrypt, getIcon } from "@/utils/tools"
import { broadcastEmitter } from "@/utils/eventbus"
import QRCode from 'qrcode'
import instance, { storeToRefs } from "@/pinia"
import { baseUrl } from "@/utils/constant"
import userInput from '../input/userInput.vue'

const { mainStore, userStore } = instance
// 定义父组件传参
const props = defineProps({
    modelValue: {
        type: Boolean,
        default: false
    }
})
// 定义父组件事件
const emits = defineEmits(['update:modelValue', 'cancel'])
// 发送登录请求的socket对象
let socket = $ref(null)
// 是否登录成功
let loginSuccess = $ref(false)
let loginByQrCode = $ref(true)
let loginForm = $ref({ username: "", password: "" })
let showUserLabel = $ref(true)
let showPwdLabel = $ref(true)
let errMsg = $ref("")
let error = $ref(false)
let capslock = $ref(false)
const showLogin = ref(false)
let islogin = $ref(false)

const { getWebInfo } = storeToRefs(mainStore)

const changeLoginType = (e) => {
    loginByQrCode = !loginByQrCode
    if (loginByQrCode) {
        getQrImg()
    } else {
        closeQrCode()
        checkCapslock(e)
    }
}

const closeQrCode = () => {
    clearQrInterval()
    if (socket) socket.close()
}

const showChangeType = computed(() => {
    return getWebInfo.value.allowUserPwd
})

const login = async () => {
    const loginfo = {
        username: loginForm.username,
        password: pbEncrypt(loginForm.password),
        platform: "console"
    }
    error = false
    errMsg = ""
    islogin = true
    const loading = useElLoading({ text: "正在登录", target: ".loginBg" })
    try {
        const result = await useRequest.post({ url: "/login", ...loginfo, formData: true, timeout: 3000 })
        if (result) {
            if (result.status) {
                loading.setText('正在加载用户数据')
                userStore.login(result.data)
                await useWait(500)
                loading.close()
                close()
                broadcastEmitter("login", true)
            } else {
                error = true
                errMsg = result.responseCode === 500 ? result.message : "用户名或密码错误"
                loading.setText('登录信息认证失败')
            }
        }
    } catch (e) {
        error = true
        if (e.timeout) {
            errMsg = "登录超时"
        } else if (e.cancel) {
            errMsg = "取消登录"
        } else {
            errMsg = "登录失败"
        }
    }
    finally {
        setTimeout(() => {
            loading.close()
            islogin = false
        }, 100)
    }
}

const submitForm = () => {
    if (islogin) return
    error = false
    errMsg = ""
    if (loginForm.username && loginForm.password) {
        login()
    } else {
        nextTick(() => {
            error = true
            errMsg = "用户名或密码不能为空"
        })
    }
}

watch(() => loginForm, () => {
    error = false
    errMsg = ""
}, { deep: true })

const focus = (flg) => {
    if (flg === 1 && !loginForm.username) {
        showUserLabel = false
    }
    if (flg === 2 && !loginForm.password) {
        showPwdLabel = false
    }
}
const blur = (flg) => {
    if (flg === 1 && !loginForm.username) {
        showUserLabel = true
    }
    if (flg === 2 && !loginForm.password) {
        showPwdLabel = true
    }
}

const clearPwd = () => {
    loginForm.password = ''
}

const checkCapslock = (event) => {
    try {
        capslock = event.getModifierState('CapsLock')
    } catch (e) {
        capslock = false
    }
}

// 关闭登录弹窗
const close = (event) => {
    if (islogin && event) return
    showLogin.value = false
    emits("update:modelValue", false)
    emits("cancel")
    closeQrCode()
}
// 二维码序列
let qrcodeSequence = $ref("")
// 二维码canvas
const qrcodeCanvas = $ref("")
// 二维码过期时间计时器
let interval = $ref(null)
// 二维码状态
let status = $ref(1)
// 二维码过期时间
let expireTime = $ref(0)
// 清除二维码计时器
const clearQrInterval = () => {
    clearInterval(interval)
}
// 请求二维码序列号,生成二维码图片
const getQrImg = () => {
    // 状态重置
    status = 1
    loginSuccess = false
    expireTime = -1
    clearQrInterval()
    const loading = useElLoading({ text: "正在生成二维码", target: ".qrcodeBox", })
    // 请求二维码序列号
    useRequest({ url: "/getLoginQrCode", source: pbEncrypt("console.qr") }).then((e) => {
        qrcodeSequence = e.data
        // 生成二维码图片
        QRCode.toCanvas(qrcodeCanvas, qrcodeSequence, {
            errorCorrectionLevel: 'H', quality: 1, margin: 0, color: {
                dark: "#008B45FF", light: "#ffffff80"
            }, width: 150, height: 150,
        }, (e) => {
            console.log(e || "二维码加载成功")
        })
        // 初始化socket
        initWebSocket()
        // 获取二维码过期时间
        expireTime = getWebInfo.value.expireTime
        // 开始计时,更新二维码过期时间
        interval = setInterval(() => {
            expireTime -= 1
            if (expireTime === 0) {
                const message = JSON.stringify({
                    status: 0,
                    message: "已过期",
                    type: "expired"
                })
                socket.send(message)
                closeQrCode()
            } else if (expireTime % 50 === 0) {
                const message = JSON.stringify({
                    status: 0,
                    message: "剩余过期时间:" + expireTime,
                    type: "heart"
                })
                socket.send(message)
            }
        }, 1000)
    }).finally(() => {
        loading.close()
    })
}

const initWebSocket = () => {
    if (typeof (WebSocket) == "undefined") {
        console.log("浏览器不支持WebSocket");
    } else {
        //实现化WebSocket对象，指定要连接的服务器地址与端口  建立连接
        //等同于socket = new WebSocket("ws://localhost:8083/checkcentersys/websocket/20");
        const wsPath = baseUrl.replace(/http/g, "ws") + "/getQrCode/" + qrcodeSequence;
        socket = new WebSocket(wsPath);
        //打开事件
        socket.onopen = function () {
            //socket.send("这是来自客户端的消息" + location.href + new Date());
        };
        //获得消息事件
        socket.onmessage = function (msg) {
            islogin = true
            try {
                const result = JSON.parse(msg.data);
                if (result.status) {
                    if (!result.data && result.message === '-1') {
                        status = -1
                    } else if (!result.data && result.message === '0') {
                        status = 0
                        closeQrCode()
                    } else if (result.data && !result.message) {
                        loginSuccess = true
                        const loading = useElLoading({ text: "正在登录", target: ".qrcodeBox" })
                        userStore.login(result.data)
                        setTimeout(() => {
                            loading.close()
                            close()
                            broadcastEmitter("login", true)
                        }, 20);
                    } else {
                        //如果过期了，关闭连接、重置连接、刷新二维码
                        closeQrCode()
                        getQrImg();
                    }
                } else {
                    closeQrCode()
                    getQrImg();
                }
            } catch (e) {
            } finally {
                islogin = false
            }

        };
        //关闭事件
        socket.onclose = function () {
            clearQrInterval()
            expireTime = 0
        };
        //发生了错误事件
        socket.onerror = function () {
            clearQrInterval()
            expireTime = 0
            //此时可以尝试刷新页面
        }
    }
}

watch(() => props.modelValue, (value) => {
    if (value) {
        showLogin.value = value
        nextTick(() => {
            getQrImg()
        })
    }
}, { immediate: true })

onMounted(() => {
    addEventListener('keydown', checkCapslock);
})

onUnmounted(() => {
    removeEventListener('keydown', checkCapslock);
})
</script>

<style scoped>
.login-container,
.loginInfo {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
}

.login-container {
    background: rgba(0, 0, 0, .5);
    z-index: 998;

}

.loginInfo {
    z-index: 999;
}

:deep(.loginInfo) {
    width: 100%;
    height: 100%;
    position: relative;
}

:deep(.loginBg) {
    position: relative;
    top: calc(30% - 50px);
    left: calc(50% - 200px);
    width: 450px;
    height: 250px;
    background: rgba(225, 225, 225, 1);
    border-radius: var(--border-radius);
    overflow: hidden;
}

:deep(.qrcode) {
    padding: 10px 10px 0 10px
}

:deep(.qrcodeBox) {
    width: 170px;
    height: 180px;
    position: absolute;
    bottom: 10px;
    right: 20px;
    background: #fff;
}

:deep(.expireTime) {
    font-size: 14px;
    color: #ff3c3c;
    height: 18px;
    display: flex;
    justify-content: center;
    align-items: center;
    position: absolute;
    left: 0;
    bottom: 0;
    width: 100%;
}

:deep(.expireModel) {
    height: 100%;
    width: 100%;
    position: absolute;
    top: 0;
    left: 0;
    background: rgba(255, 255, 255, .9)
}

:deep(.expireSecond) {
    width: 50px;
    font-size: 20px;
    text-align: center;
    letter-spacing: 2px
}

:deep(.title) {
    text-align: center;
    font-size: 35px;
    height: 60px;
    color: var(--font-color);
    width: calc(100% - 50px);
}

:deep(.change) {
    width: 50px;
    font-size: 16px;
}

:deep(.loginTypeContainer) {
    height: calc(100% - 65px);
    margin-top: 5px;
}

:deep(.loginByQrcode) {
    height: 100%;
    position: relative;
}

:deep(.tip) {
    font-size: 20px;
    color: var(--font-color);
    height: 160px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 255px;
}

:deep(.expireTip) {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: calc(100% - 19px);
    display: flex;
    justify-content: center;
    align-items: center;
    text-align: center;
    line-height: 30px;
    font-size: 20px;
}

:deep(.el-loading-spinner .el-loading-text) {
    color: rgba(65, 110, 110, .8);
}

:deep(.qrCodeLogo) {
    width: 40px;
    height: 40px;
    position: absolute;
    z-index: 0;
    left: 65px;
    top: 65px;
    pointer-events: none;
}


:deep(.item) {
    width: 100%;
    height: 40%;
    display: flex;
    justify-content: center;
    align-items: center;
}

:deep(.item .label) {
    position: absolute;
    font-weight: 400;
    height: 20px;
    line-height: 20px;
    max-width: calc(100% - 20px);
    pointer-events: none;
    color: #afb6be;
}

:deep(.item input),
:deep(.item .input) {
    display: block;
    width: calc(100% - 20px);
    border: 0;
    padding: 10px;
    font-size: 18px;
    color: #839300;
    outline: none;
    background-color: transparent !important;
    font-family: inherit;
    -webkit-text-fill-color: #839300;
    caret-color: #839300;
    transition: background-color .5s ease-in 0s;
}

:deep(.item input#pwd) {
    width: calc(100% - 55px);
}

:deep(.blur-input) {
    top: 11px;
    left: 10px;
    font-size: 20px;
    transition: all .3s ease-in-out;
}

:deep(.up-label) {
    transform: translateY(-28px);
    font-size: 16px;
    transition: all .2s ease-in-out;
    color: #839300 !important;
}

:deep(.loginByPassword) {
    height: 100%
}

:deep(.login-input) {
    position: relative;
    width: 95%;
    border-bottom: 1px solid #afb6be;
}

:deep(.login-button) {
    font-size: 34px;
    background: rgb(166 166 166 / 50%);
    border-radius: 50%;
    position: absolute;
    top: 5px;
    right: 0;
}

:deep(#pwd) {
    -webkit-text-security: disc;
    font-family: monospace;
}

:deep(.message) {
    width: 100%;
    position: relative;
    height: 20%;
}


:deep(.capital) {
    font-size: 15px;
    position: absolute;
    top: 0;
    left: 20px;
}

:deep(.error) {
    font-size: 15px;
    position: absolute;
    top: 0;
    right: 20px;
    left: unset;
}

:deep(.rotate-180) {
    transition: all .3s;
    transform: rotate(180deg);
    transform-origin: center;
    cursor: pointer;
}

:deep(.rotate-0) {
    transition: all .3s;
    transform: rotate(0deg);
    transform-origin: center;
    cursor: pointer;
}
</style>